Phlegmulator / PHLEGM  ·  Engineering issue report

APU audio polarity & hardwired analog-path inconsistency

An NES-specific analog output model is embedded in a general-purpose audio library, while the signal path contains no explicit inversion corresponding to the original NES-001 output stage. This report separates confirmed code facts from the hardware-polarity hypothesis and proposes a definition-driven correction.

Confirmed: hardcoded analog behaviorPolarity mismatch: probable, not measuredNo patch applied

Issue statement

PHLEGM v9 r82 correctly computes a positive-going, nonlinear 2A03 APU DAC mix, then routes it through hardcoded NES filters in sound.bi and writes same-sign PCM. The source shows no configurable NES analog output polarity and no explicit sign inversion in that path. Because the NES-001 schematic includes an inverting output stage, there is a plausible absolute electrical-polarity mismatch at the emulated output boundary.

What is confirmed

The NES filter frequencies and coefficients are fixed in the reusable host audio code. The inspected APU-to-PCM path does not explicitly negate the waveform.

What still needs testing

Whether PHLEGM PCM is opposite in absolute polarity to a precisely identified NES-001 RCA-output reference. Speaker acoustic pressure cannot be inferred from software sample signs alone.

Impact: Code structure violates the project's definition-driven machine architecture, and missing hardware-stage polarity may complicate hardware waveform comparisons or phase-coherent mixing. It does not by itself explain low frame rate, incorrect audio pitch, or timing-related distortion.

02. Actual versus intended signal path

Observed in r82

Recommended architecture

Illustrative polarity comparison Same rectangular waveform above zero before the analog inverter becomes an equal-amplitude negative-going waveform after inversion. Illustrative only, not measured NES audio. Before output stageIllustrative + signal After inverting stageIllustrative − signal +V−V
Concept only: y = −x reverses sample sign without reversing oscillator sequencing or changing frequency. The original NES output circuit, not an assumed speaker motion, is the proposed electrical reference.

03. Reproducible source evidence

The excerpts below are transcribed from the inspected PHLEGM v9 r82 Full Archive Set. Line numbers identify their actual locations inside the archive; excerpts are shortened to the lines material to this issue.

A. The native APU mixer returns a positive-going sum

src/lib/native-support.bi · lines 1126–1134
Dim dmcValue As Integer=gNativeApuDmcOutput(chipIndex) And &h7F
NativeApuEnsureMixerTables()
...
Dim mixed As Double=gNativeApuPulseMix(pulseIndex)+gNativeApuTndMix(tndIndex)
If mixed>1 Then mixed=1
Return mixed

Its tables use the familiar NESdev pulse and TND nonlinear DAC approximations (lines 1086–1107). This mixer is not inherently wrong: the lookup formulas describe output magnitude and DAC behavior, not the final console's absolute line-out polarity.

B. NES analog filters are hardwired into a generic library

src/lib/sound.bi · lines 60–70 and 451–462
' NES front-panel/RCA analog reconstruction.
Const SOUND_NES_HP90_COEFFICIENT = 0.9873395160400396
Const SOUND_NES_HP440_COEFFICIENT = 0.9410087476560546
Const SOUND_NES_LP14K_COEFFICIENT = 0.7828654653395070
...
If nativeAudio Then
    Dim hp90 As Double=SOUND_NES_HP90_COEFFICIENT* _
        (gSoundNesHp90Output + mixed - gSoundNesHp90Input)
    ...
    gSoundNesLp14kOutput += SOUND_NES_LP14K_COEFFICIENT* _
        (hp440-gSoundNesLp14kOutput)
    mixed=gSoundNesLp14kOutput
End If

Hardwire issue: The coefficients, NES-specific stage selection, and storage of its filter state reside in common host audio code rather than being selected from machine/chipset definitions. The nativeAudio condition derives from the presence of the nes-apu-io-v1 runtime (native-support.bi, lines 1137–1139), not an analog wiring profile.

C. PCM conversion preserves the final filter's sign

src/lib/sound.bi · lines 467–476
Dim dcCoefficient As Double=0.995, deviceGain As Double=1.0
If gMachineAudioDevice=SOUND_DEVICE_PIEZO Then _
    dcCoefficient=0.92: deviceGain=0.80
Dim filtered As Double=mixed-gSoundDcInput+ _
    (dcCoefficient*gSoundDcOutput)
...
gSoundWaveData(bufferIndex,sampleIndex)=CShort(filtered*30000.0)

The common transducer model also contains hardcoded gain and coupling parameters. This last PCM assignment does not invert the signed waveform. There is likewise no explicit sign inversion in the inspected native sample queue or its sample-generation path.

D. The NES machine definition contains no analog polarity choice

cfg/machine/nintendo/NES-NTSC/machine.config.phlegm · lines 69–76
audio_output_channels=1
audio_mix=mono
audio_device_default=internal speaker
apu_channels=pulse-1,pulse-2,triangle,noise,dmc
sound_enabled_default=true
sound_count=0

The definition describes audio routing and channel selection, but does not specify the console's analog stage, polarity, or filter topology. The support-chip definition supplies native_runtime=nes-apu-io-v1 rather than output-stage wiring.

04. Why this is an inconsistency

ConcernActual r82 behaviorRequired separation
2A03 DACNonlinear digital-channel mix implemented in a reusable native runtime.Keep the DAC algorithm independent from the motherboard's electrical output circuit.
Analog filtersNES 90 Hz / 440 Hz high-pass and 14 kHz low-pass constants embedded in sound.bi.Make filter selection/parameters property of the selected output profile, not the generic host layer.
PolarityNo explicit inversion between native mixer and PCM emission.Represent any hardware output inversion explicitly and apply it exactly once at the correct signal node.
Other machinesGeneric sound code knows about a particular NES output circuit.Retain reusable signal processing without applying NES-specific hardware assumptions to PET, SNES, or other machines.
Important polarity distinction. The NES-001 schematic depicts a 74HC04-based inverting analog stage (U9E) after the APU DAC signals are combined. That supports a hardware-stage inversion relative to that upstream node. It does not prove which sign should correspond to positive air pressure at an arbitrary user's speaker. The report targets output electrical waveform polarity relative to an identified NES-001 RCA measurement.

05. Recommended definition-driven remediation

  1. Identify the output reference. Use a named motherboard revision (for example, NES-001), named output node (RCA audio) and a known stimulus. Confirm inversion using its schematic and, ideally, a recording captured with a known noninverting acquisition path.
  2. Declare analog wiring in .phlegm. Let the machine specify which generic stage chain is attached to its APU (polarity, gain, coupling, and filter parameters). Do not embed NES board revisions or constants in the generic host audio library.
  3. Reuse generic DSP primitives. Compile/load the chain when a machine or session is loaded, with state initialized on machine/reset boundaries. Run filtering and sign selection with minimal per-sample overhead.
  4. Apply inversion at the modeled hardware node. Invert the combined signal only after all signals that physically enter that stage have been summed; never reverse individual pulse/triangle/noise/DMC bit patterns. Avoid a second inversion in PCM export.
  5. Preserve unaffected machines. Define a noninverting default stage for profiles that do not declare one, and migrate existing machines explicitly rather than silently changing their audio.

Illustrative configuration — proposed keys, not existing syntax

# Proposed machine-config concept; parser support does NOT exist in r82:
audio_output_stage=generic-analog-v1
audio_output_polarity=inverted
audio_highpass_hz=90,440
audio_lowpass_hz=14000
audio_output_gain=1.0

Actual stage ordering, sample-rate coefficient calculation, and expansion-audio routing must be derived from the target hardware and integrated with the existing .phlegm descriptor/parser conventions. This sketch is intentionally not a drop-in patch.

Scope control: Treat channel synthesis, microcode sequencing, APU timers and cycle accounting as unrelated unless testing reveals a separate defect. A sign inversion should not be used to hide pitch, clipping or scheduling problems.

06. Verification and acceptance criteria

  1. Trace the sign: Capture deterministic, signed test samples before APU DAC output, immediately before the analog stage, after that stage, and just before PCM emission. Prove exactly one intentional sign change where configured.
  2. Reference hardware: Capture the same simple pulse/step test on a specified NES-001 at RCA out. Account for high-pass settling, DC offset, capture-system inversion, recording gain and any phase shift from filters. Compare waveform orientation, not only how it sounds.
  3. Check non-destructive behavior: Confirm that inversion does not change pitch, period, RMS magnitude beyond tolerance, channel envelopes, or frame/CPU/APU timing; the filtered waveform may differ while the test is switching stage models.
  4. Preserve all routed signals: Test native APU-only audio, any configured external audio mixing, and the expected cancellation/reinforcement relationships across a common analog stage.
  5. Cross-machine regression: Validate NES-NTSC, PET/CBM sound routes and machines without an APU. Check clean reset/session restore, output-device selection and silence behavior.
  6. Packaging and portability: Verify the same data-driven profile behavior on every affected Windows/Linux host backend; no hardware behavior should depend on graphics backend or word size.
Pass — design

No NES-specific analog coefficients or mandatory polarity assumptions remain in the generic host audio output path; machine definitions choose the appropriate processing chain.

Pass — accuracy

NES electrical output waveform orientation agrees with the documented reference within an explicit measurement tolerance, and unrelated machines show no unintended sign changes.

Status: Source review completed; no executable build, analog measurement, patch, or regression test is claimed by this document.

07. Sources and provenance

Everything required to read this report is embedded in this file: styles, diagram, source excerpts and explanations. The optional links below are reference pointers for online follow-up.

  • Inspected PHLEGM source: PHLEGM-v9-r82-Full-Archive-Set-Text-Newline-Normalization-2026-1024-1525-3132.zip; SHA-256 6c25d8c5742cd01882f5bcdbb6ca7d07bdfe9cbdf452ef7d3b87f1b5ba7ddbdb. Relevant archive paths and line numbers appear in Section 03.
  • NESdev Wiki, APU Mixer: defines the nonlinear pulse_out + tnd_out approximation used to interpret the source code. www.nesdev.org/wiki/APU_Mixer.
  • NES-001 board schematic, RetroTechCollection: documents the audio mixing and output network near the inverting U9E stage. retrotechwiki.com/NES-001_Schematic. This schematic describes a particular console model; it is not a universal assertion about all NES/Famicom revisions.
  • NES schematics, independently traced: github.com/schenkzoola/NES; includes NES-001 and HVC-001 references.
  • NESdev, MMC5 audio polarity: useful distinction between waveform-generator logic and inverting a completed analog signal. forums.nesdev.org/viewtopic.php?t=11432. This is background, not proof of the NES-001 line-out polarity by itself.

Final disposition: The hardwired analog-path inconsistency is confirmed by source inspection. The missing explicit inverter is also confirmed in the inspected software path; labeling its output definitively “wrong polarity” remains pending reference electrical measurement. The recommended fix is to model output-stage wiring in data and execute it through a reusable audio pipeline.